/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2016年11月18日
 * V4.0
 */
package com.jphenix.standard.servlet;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import com.jphenix.driver.cluster.IClusterTrigger;
import com.jphenix.driver.cluster.ServerInfoVO;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;

/**
 * 集群信息支持接口
 * 
 * 2018-05-25 增加了 group() 获取当前服务器所在分组名方法
 * 2018-06-28 增加了判断本次请求是否由集群内部服务器发起的方法
 * 2018-07-26 增加了返回当前服务器时间校准差值
 * 2018-12-13 增加啊了routeCall方法，供脚本内部调用外部服务器脚本方法
 * 2019-01-31 在调用集群方法时，把调用者类信息传入，方便在日志中查看调用者
 * 2019-06-26 去掉了返回序列号头值方法，改为返回服务器索引值
 * 2019-10-08 增加了一个方法
 * 2019-12-10 增加了集群内，通过路由中转上传文件功能
 * 
 * @author MBG
 * 2016年11月18日
 */
@ClassInfo({"2019-12-10 17:47","集群信息支持接口"})
@BeanInfo({"clusterfilter"})
public interface ICluster {
	
	/**
	 * 获取当前服务器的名字
	 * @return 当前服务器名字
	 * 2016年11月19日
	 * @author MBG
	 */
	String name();
	
	/**
	 * 获取当前服务器所在的群组
	 * @return 当前服务器所在的群组
	 * 2017年4月21日
	 * @author MBG
	 */
	String group();
	
	/**
	 * 集群服务器时间
	 * @return 以主服务器为参考修正了当前服务器时间后的时间（毫秒）
	 * 2016年11月19日
	 * @author MBG
	 */
	long time();
	
	/**
	 * 返回当前服务器时间校准差值
	 * @return 当前服务器时间校准差值
	 * 2018年7月26日
	 * @author MBG
	 */
	long amendTime();
	
	/**
	 * 判断是否由集群成员调用的
	 * 动作上下文（request，response）是从线程会话中获取的。
	 * 所以不支持服务类，只支持动作类
	 * IServletConst.KEY_ACTION_CONTEXT
	 * @return 是否由集群成员调用
	 * 2016年11月20日
	 * @author MBG
	 */
	boolean check();
	
	/**
	 * 判断是否由集群成员调用的
	 * 动作上下文（request，response）是从线程会话中获取的。
	 * 所以不支持服务类，只支持动作类
	 * IServletConst.KEY_ACTION_CONTEXT
	 * @param ac 动作上下文
	 * @return 是否由集群成员调用
	 * 2016年11月20日
	 * @author MBG
	 */
	boolean check(IActionContext ac);
	
	/**
	 * 返回当前服务器是否为主服务器
	 * @return 当前服务器是否为主服务器
	 * 2016年11月21日
	 * @author MBG
	 */
	boolean master();
	
	/**
	 * 调用主服务器动作
	 * 注意：不能用集群类调用本机服务，否则会导致死循环
	 * @param script    脚本主键
	 * @param param     传入参数
	 * @param callType  调用方式  0普通调用  1调用调用主服务器  2广播调用
	 * @return          返回结果
	 * @throws          异常
	 * 2016年11月20日
	 * @author MBG
	 */
	<T> T call(String script,Object param,int callType) throws Exception;
	
	/**
	 * 调用主服务器动作
	 * 注意：不能用集群类调用本机服务，否则会导致死循环
	 * @param script    脚本主键
	 * @param callType  调用方式  0普通调用  1调用调用主服务器  2广播调用
	 * @return          返回结果
	 * @throws          异常
	 * 2016年11月20日
	 * @author MBG
	 */
	<T> T call(String script,int callType) throws Exception;
	
	/**
	 * 广播集群中所有服务器（集群类中不包括本机服务器，本机服务器由原程序执行，否则会导致死循环）
	 * @param scriptId  脚本主键
	 * @param param     参数对象
	 * 2016年11月21日
	 * @author MBG
	 */
	void callAll(String scriptId,Object param);
	
	/**
	 * 广播集群中所有服务器 （集群类中不包括本机服务器，本机服务器由原程序执行，否则会导致死循环）
	 * @param subUrl    脚本主键  动作路径（相对路径，无需域名与上下文路径）
	 * @param callSelf  是否包含当前服务器
	 * 2016年11月21日
	 * @author MBG
	 */
	void callAll(String scriptId);
	
	/**
	 * 是否为集群模式
	 * @return 是否为集群模式
	 * 2016年11月28日
	 * @author MBG
	 */
	boolean clusterMode();
	
	/**
	 * 集群服务器索引（当前服务器在集群服务器中的位置）
	 * @return 集群服务器索引
	 * 2016年12月11日
	 * @author MBG
	 */
	int index();
	
	/**
	 * 是否禁用
	 * @return 是否禁用
	 * 2017年4月26日
	 * @author MBG
	 */
	boolean disabled();
	
	/**
	 * 获取指定群组中的主服务器
	 * @param groupName 群组名
	 * @return 主服务器信息
	 * 2017年4月21日
	 * @author MBG
	 */
	ServerInfoVO getMasterServer(String groupName);
	
	/**
	 * 判断请求
	 * @param req     页面请求
	 * @return            是否允许访问
	 * 2017年5月2日
	 * @author MBG
	 */
	boolean allow(IRequest req);
	
	/**
	 * 判断请求
	 * @param req     客户端IP地址
	 * @return            是否允许访问
	 * 2017年5月2日
	 * @author MBG
	 */
	boolean allow(String ip);
	
	/**
	 * 判断本次调用是否由集群中的服务器发起
	 * @param req    请求对象
	 * @return       是否由集群中的服务器发起
	 * 2018年6月28日
	 * @author MBG
	 */
	boolean callFromCluster(IRequest req);
	
	/**
	 * 调用其他服务器上的脚本程序
	 * @param keyName              目标群组名或者指定服务器名
	 * @param scriptId                目标脚本名
	 * @param param                 传入参数
	 * @param callType               调用方式  0主服务器调用  1广播调用
	 * @param invoker              调用者
	 * @param noReturnValue     是否不需要返回值 
	 *                                        （因为是目标服务器，本机是无法通过脚本信息类判定目标脚本带不带返回值，只能手工强制无需返回值） 
	 *                                         这中情况主要用于反向应触发调用服务，只要通知前置机，无需获取返回值。这样就无需轮询等待返回值，使远程调用
	 * @return            调用返回值
	 * @throws Exception  异常
	 * 2017年4月22日
	 * @author MBG
	 */
	Object routeCall(
			String keyName
			,String scriptId
			,Object param
			,int callType
			,Object invoker
			,boolean noReturnValue) throws Exception;
	
	
	/**
	 * 以文件上传方式
	 * @param keyName         目标服务器主键或目标群组主键
	 * @param scriptId        调用目标脚本
	 * @param params          提交参数字符串 key1=val1&key2=val2
	 * @param uploadNameList  上传文件对象名（目标脚本参数名）序列
	 * @param fileNameList    源文件名序列
	 * @param filePathList    需要上传的文件绝对路径序列
	 * @param invoker         调用者
	 * @return                返回字符串信息
	 * @throws Exception      异常
	 * 2019年12月10日
	 * @author MBG
	 */
	String routeUpload(
			 String keyName
			,String scriptId
			,String params
			,List<String> uploadNameList
			,List<String> fileNameList
			,List<String> filePathList
			,Object invoker) throws Exception;
	
	/**
	 * 获取指定服务器（或指定群组中负载均衡分配的服务器url）
	 * @param serverName 服务器名，或群组名（由群组分配一台服务器返回ｕｒｌ）
	 * @return　　　　　　　服务器ｕｒｌ
	 * 2019年8月3日
	 * @author MBG
	 */
	String getServerUrl(String serverName);
	
	
	/**
	 * 调用目标服务器
	 * @param serverKey  服务器主键（名）
	 * @param scriptId   脚本主键
	 * @param param      传入参数
	 * @param callType   调用方式  0普通集群调用  1调用调用主服务器  2广播调用
	 * @return 返回信息
	 * @throws Exception 异常
	 * 2017年3月29日
	 * @author MBG
	 */
	Object call(String serverKey,String scriptId,Object param,int callType) throws Exception;
	
	
	/**
	 * 调用目标内部触发器 (调用的目标只能是当前同样的类）
	 * @param siVO         目标服务器信息类
	 * @param param        提交参数对象
	 * @param header       提交报文头对象
	 * @param contentType  0html  1xml  2json 3文件
	 * @param caller       调用者（当前类实例）
	 * @return             返回数据流
	 * 2019年10月8日
	 * @author MBG
	 */
	InputStream triggerCall(ServerInfoVO siVO,Object param,Map<String,String> header,int contentType,IClusterTrigger caller) throws Exception;
}